home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / gnome-games-data / glchess / main.py < prev    next >
Encoding:
Python Source  |  2009-04-14  |  24.7 KB  |  768 lines

  1. # -*- coding: utf-8 -*-
  2. """
  3. """
  4.  
  5. __author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
  6. __license__ = 'GNU General Public License Version 2'
  7. __copyright__ = 'Copyright 2005-2006  Robert Ancell'
  8.  
  9. __all__ = ['Application']
  10.  
  11. import sys
  12. import os
  13. import errno
  14. from gettext import gettext as _
  15. import traceback
  16. import time
  17.  
  18. import config
  19. import ui
  20. import gtkui
  21. import game
  22. import player
  23. import chess.board
  24. import chess.lan
  25. import chess.pgn
  26. import ai
  27. import network
  28. import display
  29. import history
  30.  
  31. from defaults import *
  32.        
  33. class PlayerTimer(ui.TimerFeedback):
  34.     """
  35.     """
  36.  
  37.     def __init__(self, game, colour, duration):
  38.         self.game = game
  39.         self.colour = colour
  40.         self.duration = duration
  41.         self.controller = game.application.ui.controller.addTimer(self, duration)
  42.     
  43.     def onTick(self, t):
  44.         """Called by ui.TimerFeedback"""
  45.         if self.colour is chess.board.WHITE:
  46.             self.game.view.controller.setWhiteTime(self.duration, t)
  47.         else:
  48.             self.game.view.controller.setBlackTime(self.duration, t)
  49.  
  50.     def onExpired(self):
  51.         """Called by ui.TimerFeedback"""
  52.         if self.colour is chess.board.WHITE:
  53.             self.game.getWhite().outOfTime()
  54.         else:
  55.             self.game.getBlack().outOfTime()
  56.  
  57. class ChessGame(game.ChessGame):
  58.     """
  59.     """
  60.     # The view watching this scene
  61.     view           = None
  62.     
  63.     # The players in the game
  64.     __movePlayer   = None
  65.     __aiPlayers    = None
  66.  
  67.     # Mapping between piece names and promotion types
  68.     __promotionMapping = {'queen': chess.board.QUEEN, 'knight': chess.board.KNIGHT, 'bishop': chess.board.BISHOP, 'rook': chess.board.ROOK}
  69.     
  70.     # TEMP
  71.     duration = 0
  72.     wT = None
  73.     bT = None
  74.  
  75.     def __init__(self, application, name):
  76.         """Constructor for a chess game.
  77.         
  78.         'application' is a reference to the glChess application.
  79.         'name' is the name of the game (string).
  80.         """
  81.         self.application = application
  82.         self.name = name
  83.         self.__aiPlayers = []
  84.         
  85.         self.fileName    = None
  86.         self.inHistory   = False
  87.         self.needsSaving = False
  88.         
  89.         # Call parent constructor
  90.         game.ChessGame.__init__(self)
  91.  
  92.         self.view = display.View(self)
  93.         self.view.controller = application.ui.controller.setView(name, self.view)
  94.         self.view.updateRotation(animate = False)
  95.  
  96.         self.view.showMoveHints(config.get('show_move_hints') is True)
  97.         self.view.showBoardNumbering(config.get('show_numbering') is True)
  98.         self.view.showSmooth(config.get('show_3d_smooth') is True)        
  99.         
  100.         # Watch for piece moves with a player
  101.         self.__movePlayer = player.MovePlayer(self)
  102.         self.addSpectator(self.__movePlayer)
  103.         
  104.         self.date = time.strftime('%Y.%m.%d')
  105.  
  106.     def addAIPlayer(self, name, profile, level):
  107.         """Create an AI player.
  108.         
  109.         'name' is the name of the player to create (string).
  110.         'profile' is the the AI profile to use (ai.Profile).
  111.         'level' is the difficulty level to use (string).
  112.         
  113.         Returns an AI player to use (game.ChessPlayer).
  114.         """
  115.         # Translators: Description of an AI player used in log window. %(name)s is replaced with
  116.         # the name of the AI player. %(game)s is replaced with the name of the game the AI player
  117.         # is in.
  118.         description = _("'%(name)s' in '%(game)s'") % {'name': name, 'game': self.name}
  119.         p = player.AIPlayer(self.application, name, profile, level, description)
  120.         self.__aiPlayers.append(p)
  121.         self.application.watchAIPlayer(p)
  122.         return p
  123.  
  124.     def addHumanPlayer(self, name):
  125.         """Create a human player.
  126.         
  127.         'name' is the name of the player to create.
  128.         
  129.         Returns a human player to use (game.ChessPlayer).
  130.         """
  131.         return player.HumanPlayer(self, name)
  132.  
  133.     def setTimer(self, duration, whiteTime, blackTime):
  134.         self.duration = duration
  135.         if duration <= 0:
  136.             return
  137.  
  138.         self.view.controller.setWhiteTime(duration, whiteTime)
  139.         self.view.controller.setBlackTime(duration, blackTime)
  140.  
  141.         self.wT = PlayerTimer(self, chess.board.WHITE, whiteTime)
  142.         self.bT = PlayerTimer(self, chess.board.BLACK, blackTime)
  143.         self.wT.controller.run()
  144.         
  145.         self.setTimers(self.wT, self.bT)
  146.         
  147.     def getHumanPlayer(self):
  148.         """Get the human player.
  149.         
  150.         Returns the human player (HumanPlayer) or None if no human players.
  151.         If both players are human the current player is returned.
  152.         """
  153.         c = self.getCurrentPlayer()
  154.         if isinstance(c, player.HumanPlayer):
  155.             return c
  156.         white = self.getWhite()
  157.         black = self.getWhite()
  158.         if c is white:
  159.             opponent = black
  160.         else:
  161.             opponent = white
  162.         if isinstance(opponent, player.HumanPlayer):
  163.             return opponent
  164.         return None
  165.  
  166.     def currentPlayerIsHuman(self):
  167.         """Test if the player to move is human.
  168.  
  169.         Returns True if the current player is human and able to move.
  170.         """
  171.         p = self.getCurrentPlayer()
  172.         return isinstance(p, player.HumanPlayer) and p.isReadyToMove()
  173.  
  174.     def squareIsFriendly(self, coord):
  175.         """
  176.         """
  177.         owner = self.getSquareOwner(coord)
  178.         if owner is None:
  179.             return False
  180.         return owner is self.getCurrentPlayer()
  181.         
  182.     def moveHuman(self, start, end):
  183.         """
  184.         """
  185.         assert(self.currentPlayerIsHuman())
  186.         p = self.getCurrentPlayer()
  187.         if p is self.getWhite():
  188.             colour = chess.board.WHITE
  189.         else:
  190.             colour = chess.board.BLACK
  191.  
  192.         # Use configured promotion type
  193.         try:
  194.             promotionType = self.__promotionMapping[config.get('promotion_type')]
  195.         except KeyError:
  196.             promotionType = chess.board.QUEEN
  197.  
  198.         # Make the move
  199.         move = chess.lan.encode(colour, start, end, promotionType = promotionType)
  200.         p.move(move)
  201.  
  202.         # Notify move
  203.         self.view.controller.setAttention(False)
  204.  
  205.     def toPGN(self, pgnGame):
  206.         """Write the properties of this game into a PGN game.
  207.         
  208.         'pgnGame' is the game to write into (pgn.PGNGame). All the tags should be unset.
  209.         """
  210.         white = self.getWhite()
  211.         black = self.getBlack()
  212.         
  213.         pgnGame.setTag(chess.pgn.TAG_EVENT, self.name)
  214.         pgnGame.setTag(chess.pgn.TAG_WHITE, white.getName())
  215.         pgnGame.setTag(chess.pgn.TAG_BLACK, black.getName())
  216.         pgnGame.setTag(chess.pgn.TAG_DATE, self.date)
  217.  
  218.         results = {game.RESULT_WHITE_WINS: chess.pgn.RESULT_WHITE_WIN,
  219.                    game.RESULT_BLACK_WINS: chess.pgn.RESULT_BLACK_WIN,
  220.                    game.RESULT_DRAW:       chess.pgn.RESULT_DRAW}
  221.         try:
  222.             value = results[self.result]
  223.         except KeyError:
  224.             pass
  225.         else:
  226.             pgnGame.setTag(chess.pgn.TAG_RESULT, value)
  227.  
  228.         rules = {game.RULE_ABANDONMENT: chess.pgn.TERMINATE_ABANDONED,
  229.                  game.RULE_TIMEOUT:     chess.pgn.TERMINATE_TIME_FORFEIT,
  230.                  game.RULE_DEATH:       chess.pgn.TERMINATE_DEATH}
  231.         try:
  232.             value = rules[self.rule]
  233.         except KeyError:
  234.             pass
  235.         else:
  236.             pgnGame.setTag(chess.pgn.TAG_TERMINATION, value)
  237.  
  238.         if self.duration > 0:
  239.             pgnGame.setTag(chess.pgn.TAG_TIME_CONTROL, str(self.duration))
  240.         if self.wT is not None:
  241.             pgnGame.setTag('WhiteTime', str(self.wT.controller.getRemaining()))
  242.         if self.bT is not None:
  243.             pgnGame.setTag('BlackTime', str(self.bT.controller.getRemaining()))
  244.  
  245.         # FIXME: AI levels
  246.         if isinstance(white, ai.Player):
  247.             (profile, level) = white.getProfile()
  248.             pgnGame.setTag('WhiteAI', profile)
  249.             pgnGame.setTag('WhiteLevel', level)
  250.         if isinstance(black, ai.Player):
  251.             (profile, level) = black.getProfile()
  252.             pgnGame.setTag('BlackAI', profile)
  253.             pgnGame.setTag('BlackLevel', level)
  254.  
  255.         moves = self.getMoves()
  256.         for m in moves:
  257.             pgnMove = chess.pgn.PGNMove()
  258.             pgnMove.move = m.sanMove
  259.             pgnMove.nag = m.nag
  260.             pgnMove.comment = m.comment
  261.             pgnGame.addMove(pgnMove)
  262.  
  263.     def animate(self, timeStep):
  264.         """
  265.         """
  266.         return self.view.scene.controller.animate(timeStep)
  267.     
  268.     def endMove(self, p):
  269.         game.ChessGame.endMove(self, p)
  270.         self.view.updateRotation()
  271.  
  272.     def remove(self):
  273.         """Remove this game"""
  274.         # Remove AI player windows
  275.         for p in self.__aiPlayers:
  276.             p.window.close()
  277.             self.application.unwatchAIPlayer(p)
  278.  
  279.         # Stop the game
  280.         self.abort()
  281.         
  282.         # Remove the game from the UI
  283.         self.application._removeGame(self)
  284.         self.view.controller.close()
  285.  
  286.     def setNeedsSaving(self, needsSaving):
  287.         """
  288.         """
  289.         # Autosaved games don't need saving
  290.         if self.inHistory:
  291.             needsSaving = False
  292.  
  293.         if self.needsSaving == needsSaving:
  294.             return
  295.         self.needsSaving = needsSaving
  296.         self.view.controller.setNeedsSaving(needsSaving)
  297.  
  298.     def save(self):
  299.         """Save this game"""
  300.         pgnGame = chess.pgn.PGNGame()
  301.         self.toPGN(pgnGame)
  302.         if self.inHistory:
  303.             # Don't bother if haven't made any significant moves
  304.             if len(self.getMoves()) < 2:
  305.                 return
  306.             self.application.history.save(pgnGame, self.fileName)
  307.         else:
  308.             try:
  309.                 f = file(self.fileName, 'w')
  310.                 lines = pgnGame.getLines()
  311.                 for line in lines:
  312.                     f.write(line + '\n')
  313.                 f.write('\n')
  314.                 f.close()
  315.             except IOError, e:
  316.                 return e.strerror
  317.  
  318.         self.setNeedsSaving(False)
  319.         self.application.logger.addLine('Saved game %s to %s' % (repr(self.name), self.fileName))
  320.         
  321. class UI(ui.UIFeedback):
  322.     """
  323.     """    
  324.     application = None
  325.     
  326.     splashscreen = None
  327.     
  328.     controller = None
  329.     
  330.     def __init__(self, application):
  331.         """
  332.         """
  333.         self.controller = gtkui.GtkUI(self)
  334.         self.application = application
  335.         
  336.         self.splashscreen = display.Splashscreen(self)
  337.         self.splashscreen.controller = self.controller.setView('', self.splashscreen, isPlayable = False)
  338.  
  339.         self.ggzConfig = network.GGZConfig()
  340.         dialog = network.GGZNetworkDialog(self)
  341.         self.networkDialog = dialog.controller = self.controller.addNetworkDialog(dialog)
  342.         for server in self.ggzConfig.getServers():
  343.             dialog.controller.addProfile(server, server.name)
  344.  
  345.     def onAnimate(self, timeStep):
  346.         """Called by ui.UIFeedback"""
  347.         return self.application.animate(timeStep)
  348.     
  349.     def onReadFileDescriptor(self, fd):
  350.         """Called by ui.UIFeedback"""
  351.         try:
  352.             handler = self.application.ioHandlers[fd]
  353.         except KeyError:
  354.             return False
  355.         else:
  356.             result = handler.read()
  357.             if result is False:
  358.                 self.application.ioHandlers.pop(fd)
  359.             return result
  360.  
  361.     def onWriteFileDescriptor(self, fd):
  362.         """Called by ui.UIFeedback"""
  363.         try:
  364.             handler = self.application.ioHandlers[fd]
  365.         except KeyError:
  366.             return False
  367.         else:
  368.             result = handler.write()
  369.             if result is False:
  370.                 self.application.ioHandlers.pop(fd)
  371.             return result
  372.  
  373.     def onGameStart(self, game):
  374.         """Called by ui.UIFeedback"""
  375.         if game.white.type == '':
  376.             w = None
  377.         else:
  378.             w = (game.white.type, game.white.level)
  379.         if game.black.type == '':
  380.             b = None
  381.         else:
  382.             b = (game.black.type, game.black.level)
  383.         g = self.application.addLocalGame(game.name, game.white.name, w, game.black.name, b)
  384.         if g is None:
  385.             return
  386.         g.inHistory = True
  387.         self.application.logger.addLine('Starting game %s between %s (%s) and %s (%s). (%i moves)' % \
  388.                                         (game.name,
  389.                                          game.white.name, str(game.white.type),
  390.                                          game.black.name, str(game.black.type), len(game.moves)))
  391.  
  392.         g.setTimer(game.duration, game.duration, game.duration)
  393.         g.start(game.moves)
  394.         
  395.     def loadGame(self, path, configure):
  396.         """Called by ui.UI"""
  397.         try:
  398.             p = chess.pgn.PGN(path, 1)
  399.         except chess.pgn.Error, e:
  400.             return e.message
  401.         except IOError, e:
  402.             return e.strerror
  403.         
  404.         # Use the first game
  405.         self.application.addPGNGame(p[0], path, configure)
  406.         
  407.         return None
  408.  
  409.     def onNewNetworkGame(self):
  410.         """Called by ui.UIFeedback"""
  411.         self.networkDialog.setVisible(True)
  412.         
  413.     def onQuit(self):
  414.         """Called by ui.UIFeedback"""
  415.         self.application.quit()
  416.  
  417. class Application:
  418.     """
  419.     """
  420.     # The glChess UI
  421.     ui = None
  422.     
  423.     # The AI types
  424.     __aiProfiles = None
  425.     
  426.     # Objects with IO keyed by file descriptor
  427.     ioHandlers = None
  428.     
  429.     # Network connections keyed by file descriptor
  430.     networkConnections = None
  431.     
  432.     # The network game detector
  433.     __detector = None
  434.  
  435.     # The game in progress
  436.     __game = None
  437.     
  438.     def __init__(self):
  439.         """Constructor for glChess application"""
  440.         self.__aiProfiles = {}
  441.         self.ioHandlers = {}
  442.         self.networkConnections = {}
  443.        
  444.         self.__detector = None#GameDetector(self)
  445.  
  446.         self.ui = UI(self)
  447.         
  448.         self.history = history.GameHistory()
  449.         
  450.         # Translators: Name of the log that displays application events
  451.         title = _('Application Log')
  452.         self.logger = self.ui.controller.addLogWindow(title, '', '')
  453.  
  454.     def addAIProfile(self, profile):
  455.         """Add a new AI profile into glChess.
  456.         
  457.         'profile' is the profile to add (ai.Profile).
  458.         """
  459.         name = profile.name
  460.         assert(self.__aiProfiles.has_key(name) is False)
  461.         self.__aiProfiles[name] = profile
  462.         self.ui.controller.addAIEngine(name)
  463.  
  464.     def getAIProfile(self, name):
  465.         """Get an installed AI profile.
  466.         
  467.         'name' is the name of the profile to get (string).
  468.         
  469.         Return the profile (ai.Profile) or None if it does not exist.
  470.         """
  471.         try:
  472.             return self.__aiProfiles[name]
  473.         except KeyError:
  474.             return None
  475.         
  476.     def watchAIPlayer(self, p):
  477.         """
  478.         """
  479.         fd = p.fileno()
  480.         if fd is not None:
  481.             self.ioHandlers[fd] = p
  482.             self.ui.controller.watchFileDescriptor(fd)
  483.  
  484.     def unwatchAIPlayer(self, p):
  485.         """
  486.         """
  487.         fd = p.fileno()
  488.         if fd is not None:
  489.             self.ioHandlers.pop(fd)
  490.             
  491.     def addGame(self, name):
  492.         if self.__game is not None:
  493.             # Save the current game to the history
  494.             if self.__game.inHistory:
  495.                 response = ui.SAVE_YES
  496.             elif self.__game.needsSaving:
  497.                 response = self.ui.controller.requestSave('Save current game?')
  498.             else:
  499.                 response = ui.SAVE_NO
  500.  
  501.             if response is ui.SAVE_YES:
  502.                 self.__game.save()
  503.             elif response is ui.SAVE_ABORT:
  504.                 return None
  505.         self.__game = ChessGame(self, name)
  506.         return self.__game
  507.  
  508.     def addLocalGame(self, name, whiteName, whiteType, blackName, blackType):
  509.         """Add a chess game into glChess.
  510.         
  511.         'name' is the name of the game (string).
  512.         'whiteName' is the name of the white player (string).
  513.         'whiteType' is a 2-tuple containing the AI profile name and difficulty level (str, str) or None for human players.
  514.         'blackName' is the name of the black player (string).
  515.         'blackType' is a 2-tuple containing the AI profile name and difficulty level (str, str) or None for human players.
  516.         
  517.         Returns the game object. Use game.start() to start the game.
  518.         """
  519.         # FIXME: Replace arguments with player objects
  520.         
  521.         # Create the game
  522.         g = self.addGame(name)
  523.         if g is None:
  524.             return None
  525.  
  526.         msg = ''
  527.         if whiteType is None:
  528.             p = g.addHumanPlayer(whiteName)
  529.         else:
  530.             (profile, level) = whiteType
  531.             p = g.addAIPlayer(whiteName, self.__aiProfiles[profile], level)
  532.         g.setWhite(p)
  533.  
  534.         if blackType is None:
  535.             p = g.addHumanPlayer(blackName)
  536.         else:
  537.             (profile, level) = blackType
  538.             p = g.addAIPlayer(blackName, self.__aiProfiles[profile], level)
  539.         g.setBlack(p)
  540.  
  541.         return g
  542.     
  543.     def addPGNGame(self, pgnGame, path, configure = False):
  544.         """Add a PGN game.
  545.         
  546.         'pgnGame' is the game to add (chess.pgn.PGNGame).
  547.         'path' is the path this game was loaded from (string or None).
  548.         
  549.         Returns the game object. Use game.start() to start the game.
  550.         """
  551.         gameProperties = ui.Game()
  552.  
  553.         gameProperties.path = path
  554.         gameProperties.name = pgnGame.getTag(chess.pgn.TAG_EVENT)
  555.         gameProperties.white.name = pgnGame.getTag(chess.pgn.TAG_WHITE)
  556.         gameProperties.black.name = pgnGame.getTag(chess.pgn.TAG_BLACK)
  557.         moves = []
  558.         for pgnMove in pgnGame.getMoves():
  559.             moves.append(pgnMove.move)
  560.         gameProperties.moves = moves            
  561.  
  562.         missingEngines = False
  563.         gameProperties.white.type = pgnGame.getTag('WhiteAI', '')
  564.         if gameProperties.white.type == '':
  565.             w = None
  566.         else:
  567.             if not self.__aiProfiles.has_key(gameProperties.white.type):
  568.                 missingEngines = True
  569.             gameProperties.white.level = pgnGame.getTag('WhiteLevel')
  570.             if gameProperties.white.level is None:
  571.                 gameProperties.white.level = 'normal'
  572.             w = (gameProperties.white.type, gameProperties.white.level)
  573.  
  574.         gameProperties.black.type = pgnGame.getTag('BlackAI', '')
  575.         if gameProperties.black.type == '':
  576.             b = None
  577.         else:
  578.             if not self.__aiProfiles.has_key(gameProperties.black.type):
  579.                 missingEngines = True
  580.             gameProperties.black.level = pgnGame.getTag('BlackLevel')
  581.             if gameProperties.black.level is None:
  582.                 gameProperties.black.level = 'normal'
  583.             b = (gameProperties.black.type, gameProperties.black.level)
  584.  
  585.         # If some of the properties were invalid display the new game dialog
  586.         if missingEngines or configure:
  587.             self.ui.controller.reportGameLoaded(gameProperties)
  588.             return
  589.  
  590.         newGame = self.addLocalGame(gameProperties.name, gameProperties.white.name, w, gameProperties.black.name, b)
  591.         if newGame is None:
  592.             return None
  593.         newGame.date = pgnGame.getTag(chess.pgn.TAG_DATE)
  594.         newGame.fileName = path
  595.         if gameProperties.moves:
  596.             newGame.start(gameProperties.moves)
  597.         else:
  598.             newGame.start()
  599.             
  600.         # Comment on each move
  601.         # FIXME: This should be done through a method so the UI can update better
  602.         moves = newGame.getMoves()
  603.         pgnMoves = pgnGame.getMoves()
  604.         for i in xrange(len(moves)):
  605.             moves[i].comment = pgnMoves[i].comment
  606.             moves[i].nag = pgnMoves[i].nag
  607.  
  608.         # Get the last player to resign if the file specifies it
  609.         result = pgnGame.getTag(chess.pgn.TAG_RESULT, None)
  610.         loser = None
  611.         if result == chess.pgn.RESULT_DRAW:
  612.             newGame.claimDraw()
  613.             if newGame.result != game.RESULT_DRAW:
  614.                 newGame.endGame(game.RESULT_DRAW, game.RULE_AGREEMENT)
  615.         elif result == chess.pgn.RESULT_INCOMPLETE:
  616.             if newGame.result != game.RESULT_IN_PROGRESS:
  617.                 print "WARNING: PGN file specifies game in progress, glChess does't..."            
  618.         elif result == chess.pgn.RESULT_WHITE_WIN:
  619.             loser = newGame.getBlack()
  620.         elif result == chess.pgn.RESULT_BLACK_WIN:
  621.             loser = newGame.getWhite()
  622.         if newGame.result == game.RESULT_IN_PROGRESS and loser is not None:
  623.             loser.resign()
  624.  
  625.         duration = 0
  626.         value = pgnGame.getTag(chess.pgn.TAG_TIME_CONTROL)
  627.         if value is not None:
  628.             timers = value.split(':')
  629.             try:
  630.                 duration = int(timers[0])
  631.             except ValueError:
  632.                 print 'Unknown time control: ' + value
  633.                 
  634.         value = pgnGame.getTag('WhiteTime', duration * 1000)
  635.         try:
  636.             whiteTime = int(value)
  637.         except ValueError:
  638.             whiteTime = duration
  639.         value = pgnGame.getTag('BlackTime', duration * 1000)
  640.         try:
  641.             blackTime = int(value)
  642.         except ValuError:
  643.             blackTime = duration
  644.         newGame.setTimer(duration, whiteTime / 1000, blackTime / 1000)
  645.         
  646.         # No need to save freshly loaded game
  647.         newGame.setNeedsSaving(False)
  648.  
  649.         return newGame
  650.  
  651.     def start(self):
  652.         """Run glChess.
  653.         
  654.         This method does not return.
  655.         """
  656.         self.logger.addLine('This is glChess %s' % VERSION)
  657.         
  658.         # Load AI profiles
  659.         profiles = ai.loadProfiles()
  660.  
  661.         for p in profiles:
  662.             p.detect()
  663.             if p.path is not None:
  664.                 self.logger.addLine('Detected AI: %s at %s' % (p.name, p.path))
  665.                 self.addAIProfile(p)
  666.  
  667.         nArgs = len(sys.argv)
  668.  
  669.         # Load existing games
  670.         if nArgs == 1:
  671.             self.__autoload()
  672.         
  673.         # Load requested game
  674.         elif nArgs == 2:
  675.             path = sys.argv[1]
  676.             import time
  677.             self.logger.addLine('loading...')
  678.             s = time.time()
  679.             try:
  680.                 p = chess.pgn.PGN(path, 1)
  681.             except chess.pgn.Error, e:
  682.                 # TODO: Pop-up dialog
  683.                 self.logger.addLine('Unable to open PGN file %s: %s' % (path, str(e)))
  684.             except IOError, e:
  685.                 self.logger.addLine('Unable to open PGN file %s: %s' % (path, str(e)))
  686.             else:
  687.                 # Use the first game
  688.                 if len(p) > 0:
  689.                     g = self.addPGNGame(p[0], path)
  690.             self.logger.addLine('loaded in %f seconds' % (time.time() - s))
  691.  
  692.         else:
  693.             # FIXME: Should be in a dialog
  694.             # Translators: Text displayed on the command-line if an unknown argument is passed
  695.             print _('Usage: %s [game]') % sys.argv[0]
  696.             sys.exit(0)
  697.  
  698.         # Start default game if no game present
  699.         if self.__game is None and len(self.__aiProfiles) > 0:
  700.             for p in profiles:
  701.                 if self.__aiProfiles.has_key(p.name):
  702.                     aiName = p.name
  703.                     break
  704.             black = (aiName, 'easy')
  705.             # Translators: Name of a human versus AI game. The %s is replaced with the name of the AI player
  706.             gameName = _('Human versus %s') % aiName
  707.             # Translators: Name of white player in a default game
  708.             whiteName =  _('White')
  709.             # Translators: Name of black player in a default game            
  710.             blackName = _('Black')
  711.             g = self.addLocalGame(gameName, whiteName, None, blackName, black)
  712.             g.inHistory = True
  713.             g.start()
  714.  
  715.         # Start UI (does not return)
  716.         try:
  717.             self.ui.controller.run()
  718.         except:
  719.             # FIXME: Isn't this done by bug-buddy?
  720.             print _("""glChess has crashed. Please report this bug to http://bugzilla.gnome.org
  721. Debug output:""")
  722.             print traceback.format_exc()
  723.             self.quit()
  724.             sys.exit(1)
  725.         
  726.     def animate(self, timeStep):
  727.         """
  728.         """
  729.         return self.__game.animate(timeStep)
  730.  
  731.     def quit(self):
  732.         """Quit glChess"""
  733.         if self.__game is not None:
  734.             if self.__game.inHistory:
  735.                 response = ui.SAVE_YES
  736.             elif self.__game.needsSaving:
  737.                 response = self.ui.controller.requestSave(_('Save game before closing?'))
  738.             else:
  739.                 response = ui.SAVE_NO
  740.  
  741.             if response == ui.SAVE_YES:
  742.                 self.__game.save()
  743.             elif response == ui.SAVE_ABORT:
  744.                 return
  745.  
  746.             # Abort current game (will delete AIs etc)
  747.             self.__game.abort()
  748.  
  749.         # Notify the UI
  750.         self.ui.controller.close()
  751.  
  752.         # Exit the application
  753.         sys.exit()
  754.         
  755.     # Private methods
  756.  
  757.     def __autoload(self):
  758.         """Restore games from the autosave file"""
  759.         (pgnGame, fileName, inHistory) = self.history.getUnfinishedGame()
  760.         if pgnGame is not None:
  761.             g = self.addPGNGame(pgnGame, fileName)
  762.             if g is not None:
  763.                 g.inHistory = inHistory
  764.  
  765. if __name__ == '__main__':
  766.     app = Application()
  767.     app.start()
  768.